/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package g360.WebXpdlValidation;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Vector;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 *
 * @author andya
 */
public class ValidatorWrapper 
        implements ErrorHandler
{
    private ArrayList<String> messages = new ArrayList<String>();
    private String validationLevel = "standard";
    private String schemaVersion = "2.1";
    private boolean bUseG360Extensions = true;
    private ValidateXpdl caller = null;
    
    public ValidatorWrapper(ValidateXpdl caller) 
    {
        this.caller = caller;
    }
    
    public ValidatorWrapper(ValidateXpdl caller, String validationLevel, String schemaVersion, String useG360Extensions) 
    {
        this.caller = caller;
        this.validationLevel = validationLevel;
        this.schemaVersion = schemaVersion;
        this.bUseG360Extensions = useG360Extensions.equals("on");
    }
    
    public boolean validate(Validator validator, 
                            Source source, 
                            String systemId) 
    {
        boolean result = false;
        try 
        {
            long timeBefore = System.currentTimeMillis();
            long memoryBefore = Runtime.getRuntime().freeMemory();
            validator.validate(source);
            long memoryAfter = Runtime.getRuntime().freeMemory();
            long timeAfter = System.currentTimeMillis();

            long time = timeAfter - timeBefore;
            long memory =  memoryBefore - memoryAfter;
            
            // this.appendMessage("Elapsed time: " + time);
            // this.appendMessage("Memory used: " + memory);
            result = true;
        }
        catch (SAXParseException e) 
        {
            // ignore
        }
        catch (Exception e) 
        {
            this.caller.appendMessage("Parse error occurred - " + e.getMessage());
            System.err.println("error: Parse error occurred - "+e.getMessage());
            Exception se = e;
            if (e instanceof SAXException) 
            {
                se = ((SAXException)e).getException();
            }
            if (se != null)
              se.printStackTrace(System.err);
            else
              e.printStackTrace(System.err);

        }
        return result;
    } // validate(Validator,Source,String,int,boolean)
    
    //
    // ErrorHandler methods
    //

    /** Warning. */
    public void warning(SAXParseException ex) throws SAXException 
    {
    	appendErrorMessage("Warning", ex);
    } // warning(SAXParseException)

    /** Error. */
    public void error(SAXParseException ex) throws SAXException {
    	appendErrorMessage("Error", ex);
    } // error(SAXParseException)

    /** Fatal error. */
    public void fatalError(SAXParseException ex) throws SAXException {
    	appendErrorMessage("Fatal Error", ex);
        throw ex;
    } // fatalError(SAXParseException)
    
    //
    // Protected methods
    //
    
    /** Composes the error message. */
    protected void appendErrorMessage(String type, SAXParseException ex)
    {
    	this.caller.appendMessage(composeErrorMessage(type, ex));
    }
    
    protected String composeErrorMessage(String type, SAXParseException ex) 
    {
    	String result = "";
        String systemId = ex.getSystemId();
        if (systemId != null) 
        {
            int index = systemId.lastIndexOf('/');
            if (index != -1)
                systemId = systemId.substring(index + 1);
        }
        else
        	systemId = "";
    	
    	result = "[" + type + "] " + systemId + ":" + ex.getLineNumber() + ":" + ex.getColumnNumber() + ": " + ex.getMessage();
    	return result;
    	
    } // composeErrorMessage(String,SAXParseException)

    public boolean validateOld(Vector<String>schemas,
                            Vector<InputStream>instanceStreams,
                            String systemId,
                            PrintWriter out)
    {
        boolean bSuccess = false;
        String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI;
        try {
            
            // Create SchemaFactory and configure
            SchemaFactory factory = SchemaFactory.newInstance(schemaLanguage);
            factory.setErrorHandler(this);
                        
            // Build Schema from sources
            Schema schema;
            if (schemas != null && schemas.size() > 0) 
            {
                final int length = schemas.size();
                StreamSource[] sources = new StreamSource[length];
                for (int j = 0; j < length; ++j) 
                {
                    boolean reportSchemaLocations = false;
                    if (reportSchemaLocations)
                    {
                        java.io.File file = new java.io.File((String)schemas.elementAt(j));
                        out.println("<p>Opening schema file: " + file.getAbsolutePath() + "</p>");
                    }
                    sources[j] = new StreamSource((String) schemas.elementAt(j));
                }
                schema = factory.newSchema(sources);
            }
            else 
            {
                schema = factory.newSchema();
            }
            
            // Setup validator and input source.
            Validator validator = schema.newValidator();
            validator.setErrorHandler(this);
            
            // Validate instance documents
            String validationSource = "dom";
            if (instanceStreams != null && instanceStreams.size() > 0) 
            {
                final int length = instanceStreams.size();
                if (validationSource.equals("sax")) {
                    // SAXSource
                    XMLReader reader = XMLReaderFactory.createXMLReader();
                    for (int j = 0; j < length; ++j) {
                        InputStream input = instanceStreams.elementAt(j);
                        SAXSource source = new SAXSource(reader, new InputSource(input));
                        bSuccess = this.validate(validator, source, systemId);
                    }
                }
                else if (validationSource.equals("dom")) {
                    // DOMSource
                    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                    dbf.setNamespaceAware(true);
                    DocumentBuilder db = dbf.newDocumentBuilder();
                    db.setErrorHandler(this);
                    for (int j = 0; j < length; ++j) 
                    {
                        InputStream input = instanceStreams.elementAt(j);
                        Document doc = db.parse(input);
                        DOMSource source = new DOMSource(doc);
                        source.setSystemId(systemId);
                        bSuccess = this.validate(validator, source, systemId);
                    }
                }
                else {
                    // StreamSource
                    for (int j = 0; j < length; ++j) {
                        InputStream input = instanceStreams.elementAt(j);
                        StreamSource source = new StreamSource(input);
                        bSuccess = this.validate(validator, source, systemId);
                    }
                }
            }
        }
        catch (SAXParseException e) 
        {
            // ignore
        }
        catch (Exception e) 
        {
            System.err.println("error: Parse error occurred - "+e.getMessage());
            if (e instanceof SAXException) {
                Exception nested = ((SAXException)e).getException();
                if (nested != null) {
                    e = nested;
                } 
            }
            e.printStackTrace(System.err);
        }
        return bSuccess;
    }
    
    public boolean validate(Vector<String>schemas, 
                            XpdlDomDocument xpdlDocument,
                            ErrorHandler errorHandler)
    {
        boolean bSuccess = false;
        String schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI;
        try 
        {
            if (errorHandler == null) errorHandler = this;
            
            // Create SchemaFactory and configure
            SchemaFactory factory = SchemaFactory.newInstance(schemaLanguage);
            factory.setErrorHandler(errorHandler);
                        
            // Build Schema from sources
            Schema schema;
            if (schemas != null && schemas.size() > 0) 
            {
                final int length = schemas.size();
                StreamSource[] sources = new StreamSource[length];
                for (int j = 0; j < length; ++j) 
                {
                    boolean reportSchemaLocations = false;
                    if (reportSchemaLocations)
                    {
                        java.io.File file = new java.io.File((String)schemas.elementAt(j));
                        this.caller.appendMessage("<p>Opening schema file: " + file.getAbsolutePath() + "</p>");
                    }
                    InputStream stream = this.caller.getResourceManager().getInputStream(schemas.elementAt(j));
                    StreamSource streamSource = new StreamSource(stream);
                    sources[j] = streamSource;
                    // sources[j] = new StreamSource((String) schemas.elementAt(j));
                }
                schema = factory.newSchema(sources);
            }
            else 
            {
                schema = factory.newSchema();
            }
            
            // Setup validator and input source.
            Validator validator = schema.newValidator();
            validator.setErrorHandler(errorHandler);
            
            // Validate instance documents

            DOMSource source = new DOMSource(xpdlDocument.getDomDocument());
            String systemId = "system id";
            source.setSystemId(systemId);
            bSuccess = this.validate(validator, source, systemId);
        }
        catch (SAXParseException e) 
        {
            // ignore
        }
        catch (Exception e) 
        {
            System.err.println("error: Parse error occurred - "+e.getMessage());
            if (e instanceof SAXException) {
                Exception nested = ((SAXException)e).getException();
                if (nested != null) {
                    e = nested;
                } 
            }
            e.printStackTrace(System.err);
        }
        return bSuccess;
    }

    public boolean validateOld(ByteArrayInputStream input, PrintWriter out)
    {
        boolean result;
        Vector<String> schemas = new Vector<String>();
        schemas.add(caller.getResourceManager().getSchemaFilePath(this.schemaVersion));
        if (this.bUseG360Extensions)
        {
            schemas.add(caller.getResourceManager().getG360SchemaFilePath(this.schemaVersion));
        }
        Vector<InputStream> instances = new Vector<InputStream>();
        instances.add(input);
        result = validateOld(schemas, instances, "", out);
        return result;
    }

    
}
